package net.csdn.business.gateway.filter;

import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import net.csdn.business.common.constant.GatewayConstants;
import net.csdn.business.common.domain.vo.oauth.IdentityVO;
import net.csdn.business.common.enums.OauthIdentityTypeEnum;
import net.csdn.business.common.oauth2.model.LoginUser;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.server.resource.BearerTokenError;
import org.springframework.security.oauth2.server.resource.BearerTokenErrors;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Auther: zhangyalei
 * @Date: 2022/12/14 14:27
 * @Description: 认证过滤器，根据路由配置，是否进行认证
 */
@Slf4j
@Component
public class AuthenticationGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthenticationGatewayFilterFactory.Config> {

    private String bearerTokenHeaderName = "Authorization";

    private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-._~+/]+=*)$", 2);

    @Autowired
    private ResourceServerTokenServices tokenServices;

    public AuthenticationGatewayFilterFactory() {
        super(AuthenticationGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        //return Arrays.asList("isAuth","scope");
        return Arrays.asList("isAuth");
    }

    @Override
    public GatewayFilter apply(AuthenticationGatewayFilterFactory.Config config) {
        return (exchange, chain) ->{
             String isAuth=config.getIsAuth();
             ServerHttpRequest request = exchange.getRequest();
             String token=this.resolveToken(request);
             if("1".equals(isAuth)&&StringUtils.isBlank(token)){
                 String message="Requires authentication";
                 BearerTokenError error = invalidTokenError(message);
                 throw new OAuth2AuthenticationException(error);
             }
             //token不为空，则加载用户信息
             if(StringUtils.isNotBlank(token)){
                 log.info("----open api acess token is:{}---- ",token);
                 String identityType=null;
                 String identityId=null;
                 //String identityName=null;
                 StopWatch stopWatch = new StopWatch();
                 stopWatch.start("authenticationManager.authenticate");
                 if(StringUtils.startsWith(token,"atp")||StringUtils.startsWith(token,"ato")){
                     OAuth2Authentication oAuth2Authentication = tokenServices.loadAuthentication(token);
                     if (oAuth2Authentication == null) {
                         throw new InvalidTokenException("Invalid token: " + token);
                     }
                     oAuth2Authentication.setAuthenticated(true);
                     LoginUser loginUser=(LoginUser)oAuth2Authentication.getPrincipal();
                     identityId=loginUser.getId();
                     identityType= OauthIdentityTypeEnum.USER.getCode();
                     //identityName=loginUser.getUsername()
                 }
                 stopWatch.stop();
                 log.info("当前任务名：{},耗时：{}ms",stopWatch.getLastTaskName(),stopWatch.getLastTaskTimeMillis());
                 //OAuth2Request oAuth2Request=oAuth2Authentication.getOAuth2Request();
                 //Set<String> scopes=oAuth2Request.getScope();
                 //String reqScope= config.getScope();
                 //log.info("当前路由校验的scope为：{}",reqScope);
                 //校验scope
                 /*if(StringUtils.isNotBlank(reqScope)){
                     Boolean validScope=false;
                     String[] scopeArray= config.getScopeArray();
                     for (String scope:scopeArray){
                         if(scopes.contains(scope)){
                             validScope=true;
                             break;
                         }
                     }
                     if(!validScope){
                         log.info("scopeCheckGatewayFilter check scope：{},",reqScope);
                         return ResponseUtils.webFluxResponseWriter(exchange.getResponse(), HttpStatus.FORBIDDEN,"forbidden","Forbidden");
                     }
                 }*/
                 //添加用户相关信息到header,传递给下游使用

                 String finalIdentityId = identityId;
                 String finalIdentityType = identityType;
                 //String finalIdentityName = identityName;
                 ServerHttpRequest newRequest = exchange.getRequest().mutate()
                         .headers(httpHeaders -> {
                             httpHeaders.remove(GatewayConstants.IDENTITY_ID);
                             //httpHeaders.remove(GatewayConstants.IDENTITY_NAME);
                             httpHeaders.remove(GatewayConstants.IDENTITY_TYPE);
                             httpHeaders.remove(GatewayConstants.TOKEN);
                             //httpHeaders.remove(GatewayConstants.USER_NAME);
                             //String userName=loginUser.getUsername();
                             //String traceId= BusinessUtil.genTraceId() ;
                             httpHeaders.add(GatewayConstants.IDENTITY_ID, finalIdentityId);
                             //httpHeaders.add(GatewayConstants.IDENTITY_NAME, finalIdentityName);
                             httpHeaders.add(GatewayConstants.IDENTITY_TYPE,finalIdentityType);
                             httpHeaders.add(GatewayConstants.TOKEN,token);
                             //httpHeaders.add(CommonConstants.TRACE_ID,traceId);
                             log.info("scopeCheckGatewayFilter add header：userId:{}", finalIdentityId);
                         }).build();
                 ServerWebExchange build = exchange.mutate().request(newRequest).build();
                 return chain.filter(build);
             }
            return chain.filter(exchange);
        };
    }


    private String resolveToken(ServerHttpRequest request) {
        String authorizationHeaderToken = this.resolveFromAuthorizationHeader(request.getHeaders());
        return authorizationHeaderToken;
        /*String authorizationHeaderToken = this.resolveFromAuthorizationHeader(request.getHeaders());
        String parameterToken = resolveAccessTokenFromRequest(request);
        if (authorizationHeaderToken != null) {
            if (parameterToken != null) {
                BearerTokenError error = BearerTokenErrors.invalidRequest("Found multiple bearer tokens in the request");
                throw new OAuth2AuthenticationException(error);
            } else {
                return authorizationHeaderToken;
            }
        } else {
            return parameterToken;
        }*/
    }

    private String resolveFromAuthorizationHeader(HttpHeaders headers) {
        String authorization = headers.getFirst(this.bearerTokenHeaderName);
        if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
            return null;
        } else {
            Matcher matcher = authorizationPattern.matcher(authorization);
            if (!matcher.matches()) {
                String message="Bearer token is malformed";
                BearerTokenError error = invalidTokenError(message);
                throw new OAuth2AuthenticationException(error);
            } else {
                return matcher.group("token");
            }
        }
    }

    private static String resolveAccessTokenFromRequest(ServerHttpRequest request) {
        List<String> parameterTokens = (List)request.getQueryParams().get("access_token");
        if (CollectionUtils.isEmpty(parameterTokens)) {
            return null;
        } else if (parameterTokens.size() == 1) {
            return (String)parameterTokens.get(0);
        } else {
            BearerTokenError error = BearerTokenErrors.invalidRequest("Found multiple bearer tokens in the request");
            throw new OAuth2AuthenticationException(error);
        }
    }

    private static BearerTokenError invalidTokenError(String message) {
        return BearerTokenErrors.invalidToken(message);
    }

    public static class Config {

        private String isAuth;

        //private String scope;

        //private String[] scopeArray;

        public Config() {
        }

        public String getIsAuth() {
            return isAuth;
        }

        public void setIsAuth(String isAuth) {
            this.isAuth = isAuth;
        }

        /*public String getScope() {
            return scope;
        }*/

        /*public void setScope(String scope) {
            this.scopeArray=scope.split(",");
            this.scope = scope;
        }*/

        /*public String[] getScopeArray() {
            return this.scopeArray;
        }*/
    }
}
